package edu.unlv.cs.ladders.entities;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import edu.unlv.cs.ladders.contraints.Phone;
import edu.unlv.cs.ladders.entities.BaseEntity;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;
import org.apache.log4j.Logger;
import org.hibernate.validator.constraints.Email;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.entity.RooEntity;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import edu.unlv.cs.ladders.entities.Gender;
import javax.persistence.Enumerated;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.transaction.annotation.Transactional;

@Entity
@RooJavaBean
@RooEntity(finders = { "findParticipantsByUsernameEquals", "findParticipantsByEnabled", "findParticipantsByEnabledAndAccountNonExpiredAndAccountNonLockedAndCredentialsNonExpiredAndUsernameEquals" })
public class Participant extends BaseEntity implements UserDetails {

    private static final long serialVersionUID = 1L;

    @Email
    private String username;

    private String password;

    private String salt;

    @ManyToMany(fetch=FetchType.EAGER)
    private Set<UserRole> roles;

    public void addRole(UserRole role) {
        if (!getRoles().contains(role)) {
            this.roles.add(role);
            this.merge();
        }
    }

    @Transactional
    public Set<UserRole> getRoles() {
        if (this.roles == null) {
            this.roles = new HashSet<UserRole>();
        }
        return this.roles;
    }

    private boolean accountNonExpired = true;

    private boolean accountNonLocked = true;

    private boolean credentialsNonExpired = true;

    private boolean enabled = true;

    public Collection<GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> result = new ArrayList<GrantedAuthority>();
        for (UserRole role : getRoles()) {
            result.add(new GrantedAuthorityImpl(role.getValue()));
        }
        return result;
    }

    @ManyToMany
    private Set<Team> teams;

    @NotNull
    @Size(max = 50)
    private String name;

    @NotNull
    @Phone
    private String phone;

    @Enumerated
    private Gender gender;

    private transient PasswordEncoder passwordEncoder;

    @Autowired
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    public PasswordEncoder getPasswordEncoder() {
        return this.passwordEncoder;
    }

    public void setPassword(String password) {
        Random r = new SecureRandom();
        byte[] saltBytes = new byte[20];
        r.nextBytes(saltBytes);
        this.salt = new String(saltBytes);
        this.password = passwordEncoder.encodePassword(password, salt);
    }

    public String toString() {
        return getName();
    }

    public Set<Team> getTeams() {
        if (this.teams == null) {
            this.teams = new HashSet<Team>();
        }
        return this.teams;
    }

    public static Participant getCurrentUser() {
        Participant currentUser = null;
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication auth = context.getAuthentication();
        try {
            currentUser = Participant.class.cast(auth.getPrincipal());
        } catch (ClassCastException e) {
            Logger.getLogger(Participant.class).debug("admin is current user");
        }
        return currentUser;
    }

    /**
     * Get this users teams where the tournament is open
     * @return
     */
    @Transient
    public Set<Team> getCurrentTeams() {
        Set<Team> openTeams = new HashSet<Team>();
        for (Team team : getTeams()) {
            Tournament tournament = team.getTournament();
            if (tournament != null && tournament.isOpen()) {
                openTeams.add(team);
            }
        }
        return openTeams;
    }

    public void addTeam(Team team) {
    	if(!isMemberOf(team)) {
            this.teams.add(team);
            this.merge();
        }
    }

    @Transient
    public String getEmailAddress() {
        return this.username;
    }
    @Transient
    public boolean isMemberOf(Team team){
    	if(this.getTeams().contains(team)){
    		return true;
    	}
    	return false;
    }
}
